
module slave_ram #(
    parameter int unsigned AXI_ID_WIDTH      = 7,
    parameter int unsigned AXI_ADDR_WIDTH    = 48,
    parameter int unsigned AXI_DATA_WIDTH    = 128,
    parameter int unsigned AXI_USER_WIDTH    = 2
)(
    input   logic                        clk,
    input   logic                        reset,

    input   logic                        req_i,
    input   logic                        we_i,
    input   logic [AXI_ADDR_WIDTH-1:0]   addr_i,
    input   logic [AXI_DATA_WIDTH/8-1:0] be_i,
    input   logic [AXI_DATA_WIDTH-1:0]   data_i,
    output  logic [AXI_DATA_WIDTH-1:0]   data_o
);


localparam int RomSize = 6;
const logic [0:RomSize-1][127:0] rom_mem = {
       128'h1c00000c_03400000_04022020_04003020,
       128'h03400000_03400000_4c000180_02c0518c,
       128'h00150004_0324018c_03bb418c_1400cf2c,
       128'h4c000180_00150007_00150006_00150005,
       128'h03400000_29c00085_03803c05_143ff004,
       128'h00000000_00000000_00000000_54000000
};


// logic [AXI_DATA_WIDTH-1:0] data_o;

/// read 
logic rom_cs;
assign rom_cs = addr_i >= 64'h1C00_0000 && addr_i < 64'h1C01_0000;
logic [AXI_ADDR_WIDTH-1:0] rom_raddr_q;
logic [AXI_DATA_WIDTH-1:0] rom_rdata_o;
always_ff @(posedge clk) begin
    if (rom_cs && req_i && ~ we_i) begin
        rom_raddr_q <= addr_i;
    end
end
assign rom_rdata_o = rom_mem[rom_raddr_q[9:4]];




/// --------------------------- UART --------------------------- ///
localparam int UARTSize = 1000;
logic [UARTSize-1:0][127:0] uart_map_mem = {
       128'h00000000_00000000_00000000_00000000,
       128'h00000000_00000000_00000000_00000000,
       128'h00000000_00000000_00000000_00000000,
       128'h00000000_00000000_00000000_00000000,
       128'h00000000_00000000_00000000_00000000,
       128'h00000000_00000000_00000000_00000000
};

/// read 
logic UART_cs;
assign UART_cs = addr_i >= 64'h1FF0_0000 && addr_i < 64'h1FF1_0000;
logic [AXI_ADDR_WIDTH-1:0] UART_raddr_q;
logic [AXI_DATA_WIDTH-1:0] UART_rdata_o;
always_ff @(posedge clk) begin
    if (UART_cs && req_i && ~ we_i) begin
        UART_raddr_q <= addr_i;
    end
end
assign UART_rdata_o = uart_map_mem[UART_raddr_q[$clog2(UARTSize)+3:4]];

always @(posedge clk) begin
     if (UART_cs && req_i && we_i) 
     begin
        if(be_i[15:0] == 16'h01) /// write 8-bit
           begin
              $write("%c", data_i[7:0]);
           end
        else if(be_i[15:0] == 16'hf) /// write 32-bit
           begin
              $write("%c", data_i[7:0]);
           end
        else if(be_i[15:0] == 16'hff) /// write 64-bit
           begin
              $write("%c", data_i[7:0]);
           end   
        else if(be_i[15:0] == 16'hf0)
           begin
              $write("%c", data_i[39:32]);
           end
        else if(be_i[15:0] == 16'hf00)
           begin
              $write("%c", data_i[71:64]);
           end
        else if(be_i[15:0] == 16'hf000)
           begin
              $write("%c", data_i[103:96]);
           end
    end
end


/// --------------------------- RAM --------------------------- ///
// localparam int RamSize = 32'h800_0000 / 16;
localparam int RamSize = 100;
// localparam int RamSize = 32'h200_0000;
logic [RamSize-1:0][127:0] ram_mem = {
       128'h1c00000c_03400000_04022020_04003020,
       128'h03400000_03400000_4c000180_02c0518c,
       128'h00150004_0324018c_03bb418c_1400cf2c,
       128'h4c000180_00150007_00150006_00150005,
       128'h03400000_29c00085_03803c05_143ff004,
       128'h00000000_00000000_00000000_54000000
};

/// read 
logic ram_cs;
assign ram_cs = addr_i >= 64'h0000_0000 && addr_i < 64'h8000_0000;
logic [AXI_ADDR_WIDTH-1:0] ram_raddr_q;
logic [AXI_DATA_WIDTH-1:0] ram_rdata_o;
always_ff @(posedge clk) begin
    if (ram_cs && req_i && ~ we_i) begin
        ram_raddr_q <= addr_i;
    end
end
assign ram_rdata_o = ram_mem[ram_raddr_q[$clog2(RamSize)+3:4]];



logic [AXI_DATA_WIDTH-1:0] ram_rdata_o1;
logic [AXI_DATA_WIDTH-1:0] ram_rdata_o2;
logic [AXI_DATA_WIDTH-1:0] ram_rdata_o3;
logic [AXI_DATA_WIDTH-1:0] ram_rdata_o4;
logic [AXI_DATA_WIDTH-1:0] ram_rdata_o5;
logic [AXI_DATA_WIDTH-1:0] ram_rdata_o6;
logic [AXI_DATA_WIDTH-1:0] ram_rdata_o7;

logic [AXI_DATA_WIDTH-1:0] ram_rdata_o10;

logic [AXI_DATA_WIDTH-1:0] ram_rdata_o14;
logic [AXI_DATA_WIDTH-1:0] ram_rdata_o15;
logic [AXI_DATA_WIDTH-1:0] ram_rdata_o16;

assign ram_rdata_o1 = ram_mem[0];
assign ram_rdata_o2 = ram_mem[1];
assign ram_rdata_o3 = ram_mem[2];
assign ram_rdata_o4 = ram_mem[3];
assign ram_rdata_o5 = ram_mem[4];
assign ram_rdata_o6 = ram_mem[5];
assign ram_rdata_o7 = ram_mem[6];

assign ram_rdata_o10 = ram_mem[9];

assign ram_rdata_o16 = ram_mem[15];
assign ram_rdata_o15 = ram_mem[14];
assign ram_rdata_o14 = ram_mem[13];




/// write
logic [AXI_DATA_WIDTH-1:0] ram_rdata_i;

always_comb begin
	ram_rdata_i = ram_mem[addr_i[$clog2(RamSize)+3:4]];
	for (int i = 0; i < $bits(be_i); i++) begin
        if (be_i[i]) begin
            ram_rdata_i[i*8+:8] = data_i[i*8+:8];
        end
    end
end

always @(posedge clk) begin 
    if (ram_cs && req_i && we_i) begin
        ram_mem[addr_i[$clog2(RamSize)+3:4]] <= ram_rdata_i;
    end
end

// always_ff @(posedge clk) begin
//     if (ram_cs && req_i && we_i) begin
//         ram_mem[addr_i[$clog2(RamSize)+3:4]] <= ram_rdata_i;
//     end
// end

/// Read Data Output
always @(*) begin 
	if (rom_cs) begin
		data_o = rom_rdata_o;
	end
    else if (UART_cs) begin
        data_o = UART_rdata_o;
    end
	else if (ram_cs) begin
		data_o = ram_rdata_o;
	end
	else begin
		data_o = '0;
	end
end

endmodule